<?php

/* function that may be used by the fop */

class fop_Object_Function {

	/* identifier of the function e.g.: FO,ET,EDI,13 */
	protected $_id;
	/* shortName */
	protected $_shortName;
	
	/* type string,data,set */
	protected $_type;
	/* contains the structure of the function for data and set types */
	protected $_structure;
	/*short description of the usage of the switch */
	protected $_description;
	/* some functions are put before the structure (like @13), others after */
	protected $_crypticLocation;
	/* some functions require a question mark */
	protected $_questionMark;
	
	protected $_creatorEmail;
	protected $_creationDate;
	
	
	protected $_foplineStructureData = '';

  
  /* definition of the nonce table */
  static protected $_dbFunctionTableName = 'functions';
  static protected $_dbFunctionIdCol = 'id';
  static protected $_dbFunctionDataCol = 'data';
  static protected $_dbFunctionCreatorEmailCol = 'creatorEmail';
  static protected $_dbFunctionCreationDateCol = 'creationDate';
  /*element contained in the message */
  static protected $_dbFunctionShortNameElement = 'shortName';
  static protected $_dbFunctionTypeElement = 'type';  
  static protected $_dbFunctionStructureElement = 'structure';
  static protected $_dbFunctionDescriptionElement = 'description';
  static protected $_dbFunctionCrypticLocationElement = 'crypticLocation';
  static protected $_dbFunctionQuestionMarkElement = 'questionMark';
      
      
  static protected $_allowedTypes = array('string','data','set');    
  static protected $_allowedCrypticLocation = array('before','after');

  static protected $_allowedRenderMode = array('FOPTable');

      
  public function __construct($id,$data = array()) {
  	
  	if(empty($data)) {
  	  //We get the data from the database
      $db = Zend_Registry::get('dt_dbs')->getDb('fop','function','key');
      $select = $db->select()->from(self::$_dbFunctionTableName);
      $select->where($db->quoteInto(self::$_dbFunctionIdCol ." = ?", $id));
      $res = $db->fetchAll($select->__toString());
    
      if(empty($res)) {
  	    require_once('fop/objects/Exception.php');
  	    throw new fop_Object_Exception('function not found',8); 
      }
    $data = $res[0];
  	}
    $this->_id = $data[self::$_dbFunctionIdCol];
    $this->_creatorEmail = $data[self::$_dbFunctionCreatorEmailCol];
    $this->_creationDate = $data[self::$_dbFunctionIdCol];
	$content = $this->decodeMessage($data[self::$_dbFunctionDataCol]);	

	$this->_shortName = $content[self::$_dbFunctionShortNameElement];
	$this->_type = $content[self::$_dbFunctionTypeElement];  
	$this->_structure = $content[self::$_dbFunctionStructureElement];
  	$this->_description = $content[self::$_dbFunctionDescriptionElement];
  	$this->_crypticLocation = $content[self::$_dbFunctionCrypticLocationElement];
  	$this->_questionMark = @$content[self::$_dbFunctionQuestionMarkElement];
      	
  }	//end constructor


  public function getId() {
    return $this->_id;	
  }
  public function asArray() {
  	$results = array();
    $results['id'] = $this->_id;
    $results['shortName'] = $this->_shortName;
    $results['type'] = $this->_type;
    $results['structure'] = $this->_structure;
    $results['description'] = $this->_description;
    $results['crypticLocation'] = $this->_crypticLocation;
    $results['questionMark'] = $this->_questionMark;	  	
    return $results;
  }
  
	/* returns all the functions */	
  static public function listing() {
    $db = Zend_Registry::get('dt_dbs')->getDb('fop','function','key');
    $select = $db->select()->from(self::$_dbFunctionTableName);
    $res = $db->fetchAll($select->__toString());  

    if(empty($res)) return array();

    //We must reinterpret the rows so it is independant to the way it is stored
    $results = array();
    foreach($res as $num => $row) {
    	$function = new fop_Object_Function('',$row);
    	$results[] = $function->asArray();	
    }
    
    return $results;
      
  } //end list
  
  
  public function render($mode)	{
	if(!in_array($mode,self::$_allowedRenderMode)) {
		require_once('fop/objects/Exception.php');
		throw new fop_Object_Exception('Mode is not an authorized render mode',3);		
	}
	
	if($mode == 'FOPTable') {
		//Start with the question mark
		$string = ($this->_questionMark)?'?':'';
		//continue with the @Id
		$string .= '@'.strToUpper($this->_id);
		//continue with the structure Data
		$string .= $this->_foplineStructureData;
		//then we tell if it is before or after
		if($this->_crypticLocation == 'before') return array('before'=> $string);
		else return array('after'=> $string);
	}
	
	
  }
  
  public function loadFoplineData($string) {
  	if($this->_type == 'string') return;
  	if($this->_type == 'data') {
  		if(strlen($string) < $this->_structure['minLength'] || strlen($string) > $this->_structure['maxLength']) {
			require_once('fop/objects/Exception.php');
			throw new fop_Object_Exception('Function data is not valid for function '.$this->_shortName.'- string provided:'.$string,15);	  				
  		}
  		$this->_foplineStructureData = strToUpper($string);
  		return;
  	}
  	if($this->_type == 'set') {
  		foreach($this->_structure as $num => $elem) {
  			if($elem == $string) {
  				$this->_foplineStructureData = $string;	
  				return;
  			}
  		}
		
		//We should have already left, there is porbably an issue
		require_once('fop/objects/Exception.php');
		throw new fop_Object_Exception('Function data is not valid for '.$this->_shortName,15);	  				
  		return;
  	}  	
  	
  }
  
  static public function create($params) {
  	
  	//We clean the data provided
  	//We validate the id
   	self::_validateId($params['id']);
  	$id = $params['id'];
 
 	//We validate the type
 	if(!in_array($params['type'],self::$_allowedTypes)) {
		require_once('fop/objects/Exception.php');
		throw new fop_Object_Exception('only upper case alpha and numerics allowed',1);			
 	}
 	$type = $params['type'];
 	$structure = array();
 	if('data' == $type) {
 		//data can be one of the following:
 		/*
	 		alphay-z
	 		alphanumy-z
	 		numy-z
	 		alphay
	 		alphanumy
	 		numy	
	 	*/
	 	if(!preg_match  (  '/^(alpha|num|alphanum)(\d{1})-?(\d{1})?$/'  , $params['structure'], $matches)){
			require_once('fop/objects/Exception.php');
			throw new fop_Object_Exception('structure not recognized',1);	 		
	 	}
	 	$structure['charset'] = $matches[1];
	 	$structure['minLength'] = $matches[2];
	 	$structure['maxLength'] = @$matches[3]; // this one may not be there for fixed length
	 	if(empty($structure['maxLength'])) $structure['maxLength'] = $matches[2];
 	} //end tpe = data
 	elseif('set' == $type) {
 		//in this case the different sets must be comma separated
 		if(empty($params['structure'])) {
			require_once('fop/objects/Exception.php');
			throw new fop_Object_Exception('structure is empty',1); 				
 		}
 		$data = preg_split(  '/,/'  ,  $params['structure']);
 		foreach($data as $num => $string) $structure[] = $string;
 	}

  	//We clean the description, only constraint is that it should be valid HTML
	require_once('htmlpurifier/library/HTMLPurifier.auto.php');
  	$config = HTMLPurifier_Config::createDefault();
  	$config->set('HTML.Allowed', '');
  	$purifier = new HTMLPurifier($config);

    $description = $purifier->purify($params['description']);

    $shortName = $purifier->purify($params['shortName']);
 	if(empty($shortName)) {
		require_once('fop/objects/Exception.php');
		throw new fop_Object_Exception('shortName must not be empty',1);  		
 	}
 	
	//we validate the crypticlocation
 	if(!in_array($params['crypticLocation'],self::$_allowedCrypticLocation)) {
		require_once('fop/objects/Exception.php');
		throw new fop_Object_Exception('cryptic location is not valid',1);  		
 	}
 	$crypticLocation = $params['crypticLocation'];

	$questionMark = (bool) $params['questionMark'];

	Zend_Registry::get('dt_log')->log('fop function: all parameters are parsed and seem ok, set it in database', Zend_Log::DEBUG);

  	


    //We check if the function is not already existing
    $existing = true;
    try{
    	$function = new fop_Object_Function($id);
    } catch(fop_Object_Exception $e) {
    	$existing = false;	
    }
    if($existing) {
  	    require_once('fop/objects/Exception.php');
  	    throw new fop_Object_Exception('function is already existing',10);     	
    }


    //OK we can create the function
	$email = Zend_Registry::get('app_user')->getEmail();
  	$creationDate = time();
  	
  	$message = self::formatData($shortName,$type,$structure,$description,$crypticLocation,$questionMark);

	Zend_Registry::get('dt_log')->log('fop: new function to create:'.$id.'-'.serialize($message), Zend_Log::INFO);
  	
  	$data = array(
      self::$_dbFunctionIdCol => $id,
      self::$_dbFunctionDataCol => self::encodeMessage($message),
      self::$_dbFunctionCreatorEmailCol => $email,
      self::$_dbFunctionCreationDateCol => $creationDate  	
  	);
    $db = Zend_Registry::get('dt_dbs')->getDb('fop','function','key');
    $db->insert(self::$_dbFunctionTableName, $data);
  	
  } //end create



	static protected function formatData($shortName,$type,$structure,$description,$crypticLocation,$questionMark) {
		$content = array();
		$content[self::$_dbFunctionShortNameElement] = $shortName;
		$content[self::$_dbFunctionTypeElement] = $type;  
		$content[self::$_dbFunctionStructureElement] = $structure;
	  	$content[self::$_dbFunctionDescriptionElement] = $description;
	  	$content[self::$_dbFunctionCrypticLocationElement] = $crypticLocation;
	  	$content[self::$_dbFunctionQuestionMarkElement] = $questionMark;	
		return $content;
	} 

  //the message is stored as a base64 gzdefalted serialized PHP
  static public function decodeMessage($data) {
	
    $decoded = @unserialize(gzinflate(base64_decode($data)));
    if(empty($decoded)) {
      require_once('fop/objects/Exception.php');
      throw new fop_Object_Exception('An issue occured while decoding message',16);
    }  
    return  $decoded;	
  }

  static public function encodeMessage($data) {
  	if(!is_array($data)) throw new fop_Object_Exception('input parameter is not an array',12); 
    $encoded = @base64_encode(gzdeflate(serialize($data),9));
    if(empty($encoded)) throw new fop_Object_Exception('An issue occured while encoding message',17); 
    return $encoded;   	
  }
	
	
	static function _validateId($id) {
		if(empty($id)) {
		  	require_once('fop/objects/Exception.php');
  	  		throw new fop_Object_Exception('Mandatory data is missing',1);	
		}
		if(preg_match('/[^A-Z0-9]/',$id)) {
		  	require_once('fop/objects/Exception.php');
  	  		throw new fop_Object_Exception('only upper case alpha and numerics allowed',1);	
		}
		if(strlen($id) > 5) {
		  	require_once('fop/objects/Exception.php');
  	  		throw new fop_Object_Exception('id can only be up to 5 characters',1);	
		}
		
		return true;	
	}


}